﻿using Microsoft.Extensions.Logging;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

namespace Project.Core.Config.Config.Mq {
    public class MqConnectionHelper : IMqConnectionHelper {

        private readonly ILogger<MqConnectionHelper> _logger;
        /*
         备注：使用数组的部分，是给消费端用的。目前生产者只设置了一个，消费者可能存在多个。
                     当然，有条件的还可以上RabbitMQ集群进行处理，会更好玩一点。
         */
        private static IConnectionFactory _connectionSendFactory;  //RabbitMQ工厂 发送端
        private static IConnectionFactory[] _connectionReceiveFactory; //RabbitMQ工厂 接收端  

        private static IConnection _connectionSend; //连接 发送端
        private static IConnection[] _connectionReceive; //连接 消费端

        public static List<MqConfigInfo> MqConfig; // 配置信息

        private static IModel _modelSend;  //通道  发送端
        private static IModel[] _modelReceive; //通道  消费端

        private static EventingBasicConsumer[] _basicConsumer;  // 事件

        /* 设置两个routingKey 和 队列名称，用来做测试使用*/
        public static int CostomerCount = 2;
        public static string[] RoutingKey = new string[] { "WeskyNet001", "WeskyNet002" };
        public static string[] QueueName = new string[] { "Queue001", "Queue002" };

        public MqConnectionHelper(ILogger<MqConnectionHelper> logger) {
            _logger = logger;

            _connectionReceiveFactory = new IConnectionFactory[CostomerCount];
            _connectionReceive = new IConnection[CostomerCount];
            _modelReceive = new IModel[CostomerCount];
            _basicConsumer = new EventingBasicConsumer[CostomerCount];

        }

        /// <summary>
        /// 生产者初始化连接配置
        /// </summary>
        public void SendFactoryConnectionInit() {
            _connectionSendFactory = new ConnectionFactory {
                HostName = MqConfig.FirstOrDefault().Host,
                Port = MqConfig.FirstOrDefault().Port,
                UserName = MqConfig.FirstOrDefault().User,
                Password = MqConfig.FirstOrDefault().Password
            };
        }

        /// <summary>
        /// 生产者连接
        /// </summary>
        public void SendFactoryConnection() {

            if (null != _connectionSend && _connectionSend.IsOpen) {
                // 已有连接
                return; 
            }
            // 创建生产者连接 
            _connectionSend = _connectionSendFactory.CreateConnection();
            if (null != _modelSend && _modelSend.IsOpen) {
                // 已有通道
                return; 
            }
            // 创建生产者通道
            _modelSend = _connectionSend.CreateModel(); 
            // 声明交换机，定义交换机名称和类型(direct)
            _modelSend.ExchangeDeclare(MqConfig.FirstOrDefault().ExchangeName, ExchangeType.Direct); 

        }

        /// <summary>
        /// 消费者初始化连接配置
        /// </summary>
        public void ReceiveFactoryConnectionInit() {
            var factories = new ConnectionFactory {
                HostName = MqConfig.FirstOrDefault().Host,
                Port = MqConfig.FirstOrDefault().Port,
                UserName = MqConfig.FirstOrDefault().User,
                Password = MqConfig.FirstOrDefault().Password
            };

            for (int i = 0; i < CostomerCount; i++) {
                _connectionReceiveFactory[i] = factories;  // 给每个消费者绑定一个连接工厂
            }
        }

        /// <summary>
        /// 消费者连接
        /// </summary>
        /// <param name="consumeIndex">消费者序号</param>
        /// <param name="exchangeName">交换机名</param>
        /// <param name="routeKey">路由名</param>
        /// <param name="queueName">队列名</param>
        public void ConnectionReceive(int consumeIndex, string exchangeName, string routeKey, string queueName) {
            _logger.LogInformation($"开始连接RabbitMQ消费者：{routeKey}");

            if (null != _connectionReceive[consumeIndex] && _connectionReceive[consumeIndex].IsOpen) {
                return;
            }
            // 创建消费者连接
            _connectionReceive[consumeIndex] = _connectionReceiveFactory[consumeIndex].CreateConnection(); 

            if (null != _modelReceive[consumeIndex] && _modelReceive[consumeIndex].IsOpen) {
                return;
            }
            // 创建消费者通道
            _modelReceive[consumeIndex] = _connectionReceive[consumeIndex].CreateModel();  

            _basicConsumer[consumeIndex] = new EventingBasicConsumer(_modelReceive[consumeIndex]);
            // 定义交换机名称和类型  与生产者保持一致
            _modelReceive[consumeIndex].ExchangeDeclare(exchangeName, ExchangeType.Direct);
            // 定义消费者队列
            _modelReceive[consumeIndex].QueueDeclare(
                         queue: queueName, //消息队列名称
                         durable: MqConfig.FirstOrDefault().Durable, // 是否可持久化，此处配置在文件中，默认全局持久化(true)，也可以自定义更改
                         exclusive: false,
                         autoDelete: false,
                         arguments: null
            );

            // 队列绑定给指定的交换机
            _modelReceive[consumeIndex].QueueBind(queueName, exchangeName, routeKey);
            // 设置消费者每次只接收一条消息
            _modelReceive[consumeIndex].BasicQos(0, 1, false); 

            StartListener((model, ea) => {
                // 接收到的消息
                byte[] message = ea.Body.ToArray(); 

                string msg = Encoding.UTF8.GetString(message);

                _logger.LogInformation($"队列{queueName}接收到消息:{msg}");
                Thread.Sleep(2000);

                _modelReceive[consumeIndex].BasicAck(ea.DeliveryTag, true);
            }, queueName, consumeIndex);

        }

        /// <summary>
        /// 消费者接收消息的确认机制
        /// </summary>
        /// <param name="basicDeliverEventArgs">事件处理程序委托</param>
        /// <param name="queueName">队列名</param>
        /// <param name="consumeIndex">消费者序号</param>
        private static void StartListener(EventHandler<BasicDeliverEventArgs> basicDeliverEventArgs, string queueName, int consumeIndex) {
            _basicConsumer[consumeIndex].Received += basicDeliverEventArgs;
            _modelReceive[consumeIndex].BasicConsume(queue: queueName, autoAck: false, consumer: _basicConsumer[consumeIndex]); // 设置手动确认。

        }

        /// <summary>
        /// 消息发布
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exchangeName">交换机名</param>
        /// <param name="routingKey">路由名</param>
        public static void PublishExchange(string message, string exchangeName, string routingKey = "") {
            byte[] body = Encoding.UTF8.GetBytes(message);
            //发布消息
            _modelSend.BasicPublish(exchangeName, routingKey, null, body);
        }

    }
}
